//
//  HLEM3u8DownloadManager.m
//  DownloadM3U8Tool_Example
//
//  Created by hle1879 on 2020/8/28.
//  Copyright © 2020 134344. All rights reserved.
//

#import "HLEM3u8DownloadManager.h"

static NSLock *_lock;

@interface HLEM3u8DownloadManager ()<WorkflowDelegate>

@property(nonatomic,assign)BOOL initialized;//是否已经初始化

@property(nonatomic,strong)NSMutableArray<HLEM3U8WorkFlow*> *workflows;//下载任务的数组

@property(nonatomic,strong)NSString *workSpace;//下载的根目录

@property(nonatomic,strong)HLEConfig *config;//下载的配置

@property(nonatomic,strong)NSLock *combineMP4Lock;//合成mp4的线程锁

@property(nonatomic,strong,readwrite)AFHTTPSessionManager *manager;

@property (nonatomic, strong)NSTimer *timer;//用于获取后台时间

@property (nonatomic, assign)UIBackgroundTaskIdentifier taskIdentifier;//后台任务标识


@end

@implementation HLEM3u8DownloadManager

///只能使用单例
+ (instancetype)standardUserDefaults{
    static HLEM3u8DownloadManager *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[super allocWithZone:NULL]init];
    });
    return instance;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    return [self standardUserDefaults];
}

+ (instancetype)alloc{
    return [self standardUserDefaults];
}

- (id)copy{
    return self;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.workflows = NSMutableArray.new;
        self.workSpace = NSHomeDirectory();
        self.config = [HLEConfig defaultConfig];
        self.combineMP4Lock = [[NSLock alloc]init];
    }
    return self;
}

- (void)initializeSDKWithConfig:(HLEConfig * _Nullable)config{
    if (config == nil) {
        self.config = [HLEConfig defaultConfig];
    }else{
        self.config = config;
    }
    
    NSURLSessionConfiguration *configuration = nil;
    if (self.config.supportBackground) {
        configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.hle.HLEM3u8DownloadManager"];
    }else{
        configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    }
    configuration.timeoutIntervalForRequest = MAX_REQUEST_TIMEOUT;
    configuration.HTTPMaximumConnectionsPerHost = self.config.maximumConnectionsPerHost;
    configuration.allowsCellularAccess = YES;
    self.manager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
    [self.manager.requestSerializer willChangeValueForKey:@"timeoutInterval"];
    [self.manager.requestSerializer setTimeoutInterval:MAX_REQUEST_TIMEOUT];
    [self.manager.requestSerializer didChangeValueForKey:@"timeoutInterval"];
    __weak typeof(self)weakSelf = self;
    [self.manager setTaskDidCompleteBlock:^(NSURLSession * _Nonnull session, NSURLSessionTask * _Nonnull task, NSError * _Nullable error) {
        __strong typeof(weakSelf)self = weakSelf;
        NSString *downloadPath = [task.currentRequest.URL absoluteString];
        if (downloadPath == nil || downloadPath.length == 0) {
            return;
        }
        for (HLEM3U8WorkFlow *flow in self.workflows) {
            for (NSURL *tsUrl in flow.task.tsArr) {
                if ([tsUrl.absoluteString isEqualToString:downloadPath]) {
                    [flow taskDidComplete:session task:task error:error];
                    break;
                }
            }
            
        }
    }];
    
    //初始化已经存在的任务
    NSDictionary *exitTasks = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"EXIT_TASK"];
    for (NSString *taskID in exitTasks.allKeys) {
        NSURL *m3u8Dir = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@",self.workSpace,[exitTasks objectForKey:taskID]]];
        NSData *data = [[NSData alloc]initWithContentsOfURL:m3u8Dir];
        HLEM3U8DownloadTask *task = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        HLEM3U8WorkFlow *flow = [[HLEM3U8WorkFlow alloc]init];
        BOOL flag = [flow initExitTask:task];
        if (flag) {
            [self.workflows addObject:flow];
        }
    }
    
    self.initialized = YES;
}

- (HLEM3U8DownloadTask *)creatTaskById:(NSInteger)taskID
                           downloadUrl:(NSString *)downloadUrl
                          fileSavePath:(NSString *)fileSavePath
                               success:(nullable void (^)(HLEM3U8DownloadTask *task))success
                               failure:(nullable void (^)(HLEM3U8DownloadTask *task,NSError *error))failure{
    if (!self.initialized) {
        NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLENoInitialized userInfo:@{NSLocalizedDescriptionKey:@"请先初始化SDK"}];
        if (failure != nil) {
            HLEM3U8DownloadTask *task = [[HLEM3U8DownloadTask alloc]init];
            task.taskID = taskID;
            dispatch_main_async_safe(^{
                failure(task,error);
            });
        }
        return nil;
    }
    NSURL *m3u8Url = [[NSURL alloc]initWithString:downloadUrl];
    if (m3u8Url.isFileURL || taskID < 0 || fileSavePath == nil || fileSavePath.length == 0) {
        NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEParametersInvalid userInfo:@{NSLocalizedDescriptionKey:@"参数非法"}];
        if (failure != nil) {
            HLEM3U8DownloadTask *task = [[HLEM3U8DownloadTask alloc]init];
            task.taskID = taskID;
            dispatch_main_async_safe(^{
                failure(task,error);
            });
        }
        return nil;
    }
    
    NSString *m3uName = m3u8Url.URLByDeletingPathExtension.lastPathComponent;
    HLEM3U8DownloadTask *task = [[HLEM3U8DownloadTask alloc]init];
    task.url = m3u8Url;
    task.taskID = taskID;
    task.name = m3uName;
    task.totalSize = 0;
    task.tsArr = NSMutableArray.new;
    task.uri = fileSavePath;
    task.supportBackground = self.config.supportBackground;
    task.downloadRetries = self.config.downloadRetries;
    task.supportBrokenPointDownload = self.config.supportBrokenPointDownload;
    
    HLEM3U8WorkFlow *flow = [[HLEM3U8WorkFlow alloc]init];
    flow.delegate = self;
    __weak typeof(self) weakSelf = self;
    [flow initTask:task success:^(HLEM3U8DownloadTask * _Nonnull task){
        __strong typeof(weakSelf) self = weakSelf;
        //内存在存放已经创建好的任务
        __block BOOL isExit = NO;
        [self.workflows enumerateObjectsUsingBlock:^(HLEM3U8WorkFlow * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if (obj.task.taskID == flow.task.taskID) {
                self.workflows[idx] = flow;
                isExit = YES;
                *stop = YES;
                return;
            }
        }];
        if (!isExit) {
            [self.workflows addObject:flow];
        }
        //本地缓存已经创建好的任务
        NSMutableDictionary *exitTask = [NSMutableDictionary dictionaryWithDictionary:[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"EXIT_TASK"]];
        NSString *m3u8Dir = [NSString stringWithFormat:@"%@/m3u8",fileSavePath];
        [exitTask setValue:m3u8Dir forKey:@(taskID).stringValue];
        [[NSUserDefaults standardUserDefaults] setValue:exitTask forKey:@"EXIT_TASK"];
        [[NSUserDefaults standardUserDefaults] synchronize];
        
        if (success != nil) {
            dispatch_main_async_safe(^{
                success(task);
            });
        }
    } failure:^(HLEM3U8DownloadTask * _Nonnull task,NSError * _Nonnull error) {
        if (failure != nil) {
            dispatch_main_async_safe(^{
                failure(task,error);
            });
        }
    }];
    return task;
    
}

- (NSArray <HLEM3U8DownloadTask *>*)getAllM3u8DownloadTask{
    if (!self.initialized) {
        return nil;
    }
    NSMutableArray *allTask = NSMutableArray.new;
    for (HLEM3U8WorkFlow *flow in self.workflows) {
        [allTask addObject:flow.task];
    }
    return allTask;
}

- (BOOL)resumeTask:(HLEM3U8DownloadTask *)task
          progress:(void (^)(HLEM3U8DownloadTask *task,NSProgress *progress, NSInteger allSize))progress
             speed:(void (^)(HLEM3U8DownloadTask *task,NSInteger speed))speed
           success:(nullable void (^)(HLEM3U8DownloadTask *task))success
           failure:(nullable void (^)(HLEM3U8DownloadTask *task,NSError *error))failure{
    if (!self.initialized) {
        NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLENoInitialized userInfo:@{NSLocalizedDescriptionKey:@"请先初始化SDK"}];
        if (failure != nil) {
            dispatch_main_async_safe(^{
                failure(task,error);
            });
        }
        MAIN_POST_NOTIFICATION(HLEDownloadFaileNotice, task)
        return NO;
    }
    if (task == nil || task.taskID < 0 || task.url == nil || task.uri == nil) {
        NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEParametersInvalid userInfo:@{NSLocalizedDescriptionKey:@"参数非法"}];
        if (failure != nil) {
            dispatch_main_async_safe(^{
                failure(task,error);
            });
        }
        MAIN_POST_NOTIFICATION(HLEDownloadFaileNotice, task)
        return NO;
    }
    HLEM3U8WorkFlow *flow = [self getFlowByTask:task];
    if (flow == nil) {
        NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLETASKNOCREATE userInfo:@{NSLocalizedDescriptionKey:@"请先创建任务"}];
        if (failure != nil) {
            dispatch_main_async_safe(^{
                failure(task,error);
            });
        }
        MAIN_POST_NOTIFICATION(HLEDownloadFaileNotice, task)
        return NO;
    }
    [flow downloadWithProgress:progress speed:speed success:success failure:failure];
    return YES;
}

- (BOOL)resumeTaskBytaskID:(NSInteger)taskID
                  progress:(void (^)(HLEM3U8DownloadTask *task,NSProgress *progress, NSInteger allSize))progress
                     speed:(void (^)(HLEM3U8DownloadTask *task,NSInteger speed))speed
                   success:(nullable void (^)(HLEM3U8DownloadTask *task))success
                   failure:(nullable void (^)(HLEM3U8DownloadTask *task,NSError *error))failure{
    HLEM3U8DownloadTask *task = [self getTaskBytaskID:taskID];
    if (task == nil) {
        NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLETASKNOCREATE userInfo:@{NSLocalizedDescriptionKey:@"请先创建任务"}];
        if (failure != nil) {
            dispatch_main_async_safe(^{
                failure(task,error);
            });
        }
        MAIN_POST_NOTIFICATION(HLEDownloadFaileNotice, task)
        return NO;
    }
    return [self resumeTask:task progress:progress speed:speed success:success failure:failure];
}


- (BOOL)pauseTask:(HLEM3U8DownloadTask *)task{
    if (task == nil || task.taskID < 0 || task.url == nil || task.uri == nil) {
        return NO;
    }
    HLEM3U8WorkFlow *flow = [self getFlowByTask:task];
    if (flow == nil) {
        return NO;
    }
    [flow pause];
    MAIN_POST_NOTIFICATION(HLEDownloadPauseNotice, task)
    return YES;
}

- (BOOL)pauseTaskBytaskID:(NSInteger)taskID{
    HLEM3U8DownloadTask *task = [self getTaskBytaskID:taskID];
    if (task == nil) {
        return NO;
    }
    return [self pauseTask:task];
}

- (BOOL)cancelTask:(HLEM3U8DownloadTask *)task{
    if (!self.initialized) {
        return NO;
    }
    if (task == nil || task.taskID < 0 || task.url == nil || task.uri == nil) {
        return NO;
    }
    HLEM3U8WorkFlow *flow = [self getFlowByTask:task];
    if (flow == nil) {
        return NO;
    }
    BOOL flag = [flow cancel];
    if (flag) {
        MAIN_POST_NOTIFICATION(HLEDownloadCancelNotice,flow.task)
        //清楚内存和磁盘上的缓存
        [self.workflows removeObject:flow];
        //删除本地的任务
        NSDictionary *exitTasks = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"EXIT_TASK"];
        NSMutableDictionary *tempTasks = [NSMutableDictionary dictionaryWithDictionary:exitTasks];
        [tempTasks removeObjectForKey:@(flow.task.taskID).stringValue];
        [[NSUserDefaults standardUserDefaults] setObject:tempTasks forKey:@"EXIT_TASK"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
    return flag;
}

- (BOOL)cancelTaskBytaskID:(NSInteger)taskID{
    HLEM3U8DownloadTask *task = [self getTaskBytaskID:taskID];
    if (task == nil) {
        return NO;
    }
    return [self cancelTask:task];
}

- (void)combineMP4ByTaskID:(NSInteger)taskID
                tsFilePath:(NSString *)tsfilepath
               mp4FilePath:(NSString *)mp4FilePath
                   success:(nullable void (^)(HLEM3U8DownloadTask *task))success
                   failure:(nullable void (^)(HLEM3U8DownloadTask *task,NSError *error))failure{
    [self.combineMP4Lock lock];
    if (!self.initialized) {
        NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLENoInitialized userInfo:@{NSLocalizedDescriptionKey:@"请先初始化SDK"}];
        HLEM3U8DownloadTask *task = [[HLEM3U8DownloadTask alloc]init];
        task.taskID = taskID;
        if (failure != nil) {
            dispatch_main_async_safe(^{
                failure(task,error);
            });
        }
        MAIN_POST_NOTIFICATION(HLECombineMP4FaileNotice,task)
        [self.combineMP4Lock unlock];
        return;
    }
    if (taskID < 0 || tsfilepath == nil || tsfilepath.length == 0 || mp4FilePath == nil || mp4FilePath.length == 0) {
        NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLEParametersInvalid userInfo:@{NSLocalizedDescriptionKey:@"参数非法"}];
        HLEM3U8DownloadTask *task = [[HLEM3U8DownloadTask alloc]init];
        task.taskID = taskID;
        if (failure != nil) {
            dispatch_main_async_safe(^{
                failure(task,error);
            });
        }
        MAIN_POST_NOTIFICATION(HLECombineMP4FaileNotice,task)
        [self.combineMP4Lock unlock];
        return;
    }
    
    HLEM3U8WorkFlow *flow = [self getFlowBytaskID:taskID];
    if (flow == nil) {
        NSError *error = [NSError errorWithDomain:@"HLEM3u8DownloadManager" code:HLETASKNOCREATE userInfo:@{NSLocalizedDescriptionKey:@"请先创建任务"}];
        HLEM3U8DownloadTask *task = [[HLEM3U8DownloadTask alloc]init];
        task.taskID = taskID;
        if (failure != nil) {
            dispatch_main_async_safe(^{
                failure(task,error);
            });
        }
        MAIN_POST_NOTIFICATION(HLECombineMP4FaileNotice,task)
        [self.combineMP4Lock unlock];
        return;
    }
    
    __weak typeof(self)weakSelf = self;
    [flow combineMP4WithFilePath:mp4FilePath success:success failure:false complete:^{
        __strong typeof(weakSelf)self = weakSelf;
        [self.combineMP4Lock unlock];
    }];
}

///WorkflowDelegate
-(void)workflowFinish:(HLEM3U8WorkFlow *)workflow{
    //删除内存中的任务
    [self.workflows enumerateObjectsUsingBlock:^(HLEM3U8WorkFlow * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.task.taskID == workflow.task.taskID) {
            [self.workflows removeObjectAtIndex:idx];
            *stop = YES;
            return;
        }
    }];
    
    //删除本地的任务
    NSDictionary *exitTasks = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"EXIT_TASK"];
    NSMutableDictionary *tempTasks = [NSMutableDictionary dictionaryWithDictionary:exitTasks];
    [tempTasks removeObjectForKey:@(workflow.task.taskID).stringValue];
    [[NSUserDefaults standardUserDefaults] setObject:tempTasks forKey:@"EXIT_TASK"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
    if (self.config.supportBackground) {
        [self comeToBackgroundMode];
    }
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
    if (self.timer) {
        [self.timer invalidate];
        self.timer = nil;
    }
}

-(void)comeToBackgroundMode{
    UIApplication*  app = [UIApplication sharedApplication];
    self.taskIdentifier = [app beginBackgroundTaskWithExpirationHandler:^{
        [app endBackgroundTask:self.taskIdentifier];
        self.taskIdentifier = UIBackgroundTaskInvalid;
    }];
    self.timer = [NSTimer scheduledTimerWithTimeInterval:25.0 target:self selector:@selector(applyForMoreTime) userInfo:nil repeats:YES];
    [self.timer fire];
}

-(void)applyForMoreTime {
    if ([UIApplication sharedApplication].backgroundTimeRemaining < 60) {
        [[UIApplication sharedApplication] endBackgroundTask:self.taskIdentifier];
        self.taskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
            [[UIApplication sharedApplication] endBackgroundTask:self.taskIdentifier];
            self.taskIdentifier = UIBackgroundTaskInvalid;
        }];
    }
}

///private私有方法
///判断当前任务是否存在
-(BOOL)taskIsExit:(NSInteger)taskID{
    BOOL isExit = NO;
    for (HLEM3U8WorkFlow *flow in self.workflows) {
        if (flow.task.taskID == taskID) {
            isExit = YES;
            break;
        }
    }
    return isExit;
}

///通过taskID获取已经存在的任务
-(HLEM3U8DownloadTask*)getTaskBytaskID:(NSInteger)taskID{
    HLEM3U8DownloadTask *task = nil;
    for (HLEM3U8WorkFlow *flow in self.workflows) {
        if (flow.task.taskID == taskID) {
            task = flow.task;
            break;
        }
    }
    return  task;
}

///通过task获取flow
-(HLEM3U8WorkFlow*)getFlowByTask:(HLEM3U8DownloadTask*)task{
    HLEM3U8WorkFlow *flow = nil;
    for (HLEM3U8WorkFlow *f in self.workflows) {
        if (f.task.taskID == task.taskID) {
            flow = f;
            break;
        }
    }
    return  flow;
}

///通过taskID获取flow
-(HLEM3U8WorkFlow*)getFlowBytaskID:(NSInteger)taskID{
    HLEM3U8WorkFlow *flow = nil;
    for (HLEM3U8WorkFlow *f in self.workflows) {
        if (f.task.taskID == taskID) {
            flow = f;
            break;
        }
    }
    return  flow;
}
@end
